package com.qunar.train.mock.dubbo;

import com.google.common.base.Charsets;
import com.google.common.io.Closer;

import com.qunar.train.mock.util.AegisLog;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

import javax.annotation.processing.Filer;
import javax.lang.model.element.Element;
import javax.tools.FileObject;
import javax.tools.StandardLocation;

import static com.google.common.base.Charsets.UTF_8;
import static com.qunar.train.mock.util.AegisLog.log;

public class GenerrateClassTool {

    public static List getContainssWordFile(List list, File des_file, final String contains_word, final String search_file, final String replace_word) {
        if (des_file == null) return null;
        if (des_file.listFiles() == null) return null;
        for (File pathname : des_file.listFiles()) {
            if (pathname.isFile()) {
                // 如果查询的方法是包含serch_file 如果有，继续判断是否包含文字contains_word
                if (pathname.getName().contains(search_file)) {
                    try {
                        FileReader fr = new FileReader(pathname);
                        BufferedReader br = new BufferedReader(fr);
                        String str = "";//
                        while (null != (str = br.readLine())) {
                            if (str.contains(contains_word)) {
                                if (!list.contains(pathname))// 添加进去
                                    list.add(pathname);
                                if (replace_word != null) {
                                    str = str.replace(contains_word, replace_word);
                                }
                            }
                        }
                        br.close();
                    } catch (FileNotFoundException e) {
                        AegisLog.error(e);
                    } catch (IOException e) {
                        AegisLog.error(e);
                    }
                }
            } else {
                list = getContainssWordFile(list, pathname, contains_word, search_file, replace_word);
            }
        }

        return list;
    }

    public static List getContainssWordFile(List list, File des_file, final String contains_word, final String search_file) {
        if (des_file == null) return null;
        if (des_file.listFiles() == null) return null;
        for (File pathname : des_file.listFiles()) {
            if (pathname.isFile()) {
                // 如果查询的方法是包含serch_file 如果有，继续判断是否包含文字contains_word
                if (pathname.getName().contains(search_file)) {
                    try {
                        FileReader fr = new FileReader(pathname);
                        BufferedReader br = new BufferedReader(fr);
                        String str = "";//
                        while (null != (str = br.readLine())) {
                            if (str.contains(contains_word)) {
                                if (!list.contains(pathname))// 添加进去
                                    list.add(pathname);
                            }
                        }
                        br.close();
                    } catch (FileNotFoundException e) {
                        AegisLog.error(e);
                    } catch (IOException e) {
                        AegisLog.error(e);
                    }
                }
            } else {
                list = getContainssWordFile(list, pathname, contains_word, search_file);
            }
        }

        return list;
    }

    public List<String> findConfigFile(String containsWord) {
        String path = this.getClass().getClassLoader().getResource("").getPath().split("/test-classes")[0];
        File dictionary;
        if (path.contains("/classes")) {
            dictionary = new File(path);
            log(dictionary.getPath());
        } else {
            dictionary = new File(path + "/classes");
            log(dictionary.getPath());
        }
        List list = GenerrateClassTool.getContainssWordFile(new ArrayList(), dictionary, containsWord, "xml");
        if (list.isEmpty()) return null;
        List<String> configRelativePaths = new ArrayList<>();
        for (Object fileName : list) {
            StringBuffer config = new StringBuffer(fileName.toString());
            String configRelativePath1 = fileName.toString().substring(config.indexOf("classes") + 8);
            String configRelativePath = configRelativePath1.replace("\\", "/");
            log(configRelativePath);
            configRelativePaths.add(configRelativePath);
        }
        log("the length of the files is :  " + list.size());
        return configRelativePaths;
    }

    public List<File> findConfigFilesReturnFile(String containsWord) {
        String path = this.getClass().getClassLoader().getResource("").getPath().split("/test-classes")[0];
        File dictionary;
        if (path.contains("/classes")) {
            dictionary = new File(path);
            log(dictionary.getPath());
        } else {
            dictionary = new File(path + "/classes");
            log(dictionary.getPath());
        }
        List list = GenerrateClassTool.getContainssWordFile(new ArrayList(), dictionary, containsWord, "xml");
//        if (list.isEmpty()) return null;
//        List <String> configRelativePaths = new ArrayList <>();
//        for (Object fileName : list) {
//            StringBuffer config = new StringBuffer(fileName.toString());
//            String configRelativePath1 = fileName.toString().substring(config.indexOf("classes") + 8);
//            String configRelativePath = configRelativePath1.replace("\\", "/");
//            log(configRelativePath);
//            configRelativePaths.add(configRelativePath);
//        }
        log("the length of the files is :  " + list.size());
        return list;
    }

    public static void createResource(Filer filer, String path, String content) {
        try {
            FileObject fileObject = filer.createResource(StandardLocation.CLASS_OUTPUT, "", path, new Element[0]);
            OutputStream out = fileObject.openOutputStream();
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, UTF_8));
            PrintWriter pw = new PrintWriter(writer, false);
            pw.println(content);
            pw.close();
            writer.flush();
            writer.close();
            out.close();
        } catch (IOException ex) {
            AegisLog.error(ex);
        }
    }

    public static List<String> readAndFilterCommit(InputStream input, String leftFlag, String rightFlag) throws IOException {
        List list = new ArrayList();
        Closer closer = Closer.create();
        try {
            BufferedReader br = closer.register(new BufferedReader(new InputStreamReader(input, UTF_8)));
            String pline = new String("");
            String dubbointerf = new String("");
            boolean flag = false;
            while ((pline = br.readLine()) != null) {
                pline = " " + pline.trim();
                if (pline.indexOf(leftFlag) != -1) {
                    if (pline.indexOf("<!--") != -1 || pline.indexOf("//") != -1) continue;
                    flag = true;
                }
                if (flag && (pline.indexOf(rightFlag) != -1 || pline.indexOf("/>") != -1)) {
                    dubbointerf += pline;
                    list.add(dubbointerf);
                    dubbointerf = "";
                    flag = false;
                }
                if (flag) {
                    dubbointerf += pline;
                }
            }
            br.close();
            return list;
        } catch (Throwable t) {
            throw closer.rethrow(t);
        } finally {
            closer.close();
        }
    }

    //用于使用Annotation Processor 修改文件
    private void modifyTestXML(Filer filer, String fileRelativeName, String contains_word, String replace_word) {
        FileObject existingFile = null;
        try {
            FileObject fileObject = filer.createResource(StandardLocation.CLASS_OUTPUT, "", fileRelativeName);
            AegisLog.info("Looking for existing resource file at " + existingFile.toUri());
            StringBuffer testXML = readTestXMLFile(fileObject.openInputStream(), contains_word, replace_word);
            AegisLog.info("Existing service entries: " + testXML);

            if (testXML != null) {
                FileObject writeFile = filer.createResource(StandardLocation.CLASS_OUTPUT, "", fileRelativeName);
                OutputStream out = writeFile.openOutputStream();
                BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, Charsets.UTF_8));
                writer.write(testXML.toString());
                writer.flush();
                out.close();
                AegisLog.info("Wrote to: " + writeFile.toUri());
            }
        } catch (IOException e) {
            AegisLog.error(e);
        }

    }

    private StringBuffer readTestXMLFile(InputStream input, String contains_word, String replace_word) throws IOException {

        Closer closer = Closer.create();
        try {
            BufferedReader r = closer.register(new BufferedReader(new InputStreamReader(input, Charsets.UTF_8)));
            StringBuffer textXML = new StringBuffer();
            String line;
            while ((line = r.readLine()) != null) {
                if (line.contains(contains_word)) {
                    line = line.replace(contains_word, replace_word);
                }
                textXML.append(line);
                textXML.append("\n");
            }
            AegisLog.info(textXML.toString());
            return textXML;
        } catch (Throwable var9) {
            throw closer.rethrow(var9);
        } finally {
            closer.close();
        }
    }

    /**
     * parse method into Function Object
     *
     * @return Function
     */
    protected static GeneratorProcessor.ClassFunction parseMethod(Method method) {
        //function with final modified can not be overrided
        if (Modifier.toString(method.getModifiers()).indexOf("public final") > -1) {
            return null;
        }

        GeneratorProcessor.ClassFunction func = new GeneratorProcessor.ClassFunction();
        func.method = method;
        func.name = method.getName();
        func.returns = WhiteList.typeFormat(method.getGenericReturnType().toString());
        func.memberName = func.name + "ReturnValue";
        func.memberDefault = GenerrateClassTool.defaultValue(func.returns);
        func.params = GenerrateClassTool.getInfoParameters(method.getGenericParameterTypes());
        return func;
    }

    /**
     * set member default value, for return functions
     *
     * @return dedault value
     */
    protected static String defaultValue(String s) {
        String res = null;
        String tmp = s;
        if (s.indexOf('.') > 0)
            tmp = s.substring(s.lastIndexOf('.') + 1);

        switch (s) {
            case "int":
            case "byte":
            case "short":
            case "char":
                res = "0";
                break;
            case "long":
                res = "0L";
                break;
            case "float":
                res = "0.0f";
                break;
            case "double":
                res = "0.0d";
                break;
            case "String":
                res = "\"\"";
                break;
            case "boolean":
                res = "false";
                break;
            case "BigDecimal":
            case "Long":
            case "Integer":
                res = tmp + "(0)";
                break;
        }
        return res;
    }


    /**
     * rename function name whith paramters to ensure unique
     */
    protected static String renameFunc(Method method) {
        String res = method.getName() + "ReturnValue";
        Class<?>[] pts = method.getParameterTypes();

        if (pts.length == 0) {
            return res += "PVoid";
        }

        res += "P";
        StringBuffer buf = new StringBuffer();
        for (Class<?> c : pts) {
            String s = c.getName();
            s = WhiteList.typeFormat(s);
            s = s.replace("[]", "s");//array 2 s
            s = s.substring(s.lastIndexOf('.') + 1).toLowerCase();
            s = s.substring(0, 1).toUpperCase() + s.substring(1);
            buf.append(s);
        }
        res = res + buf.toString();
        return res;
    }

    /**
     * 继承类构造函数mock
     */
    protected static StringBuilder getInfoInnerClass(Class<?> clazz, String mockName, String interfaceName, String clsType) {
        StringBuilder info = new StringBuilder("");
        /** super class 构造函数mock */
        if (clsType.equals("class")) {
            info.append("\tpublic static class ").append(mockName.substring(4)).
                    append("Mock extends MockUp<").append(interfaceName).append("> {\n");
            Constructor[] constructors = clazz.getDeclaredConstructors();
            for (Constructor constructor : constructors) {
                info.append("\t\t@Mock\n\t\t");
                info.append(Modifier.toString(constructor.getModifiers())).append(" void $init(");
                info.append(getInfoParameters(constructor.getGenericParameterTypes())).append(") {}\n");
            }
            info.append("\t\tpublic static void clear(){}\n");
            info.append("\t}\n\n");
        }
        return info;
    }

    /**
     * 生成函数参数字符串 jdk1.8可直接用Parameter对象
     */
    protected static String getInfoParameters(Type[] types) {
        String res = "";
        if (types.length != 0) {
            StringBuffer buf = new StringBuffer();
            int i = 0;
            for (Type type : types) {
                buf.append(WhiteList.typeFormat(type.toString()) + " arg" + i++ + ", ");
            }
            res += buf.toString();
            res = res.substring(0, res.length() - 2);
        }
        return res;
    }

    /**
     * import contents
     */
    protected static String getInfoImport(String classType) {
        StringBuilder info = new StringBuilder("import java.util.*;\n");
        if (classType.equals("class")) {
            info.append("import mockit.Mock;\n");
            info.append("import mockit.MockUp;\n");
        }
        return info.toString();
    }

    /**
     * 判断类的mock是否需要继承方式, 根据被mock类的修饰符决定
     */
    protected static Boolean isNeedExtends(Class<?> clazz) {
        Boolean res = false;
        String modifier = Modifier.toString(clazz.getModifiers());
        if (modifier.equals("public")) res = true;
        return res;
    }

    /**
     * 泛型返回类型计算
     */
    protected static String typeFormat(Class<?> returnType, Type genericReturnType) {
        String res = "";
        if (genericReturnType instanceof TypeVariable) {
            if (returnType.getName().equals("java.lang.Object")) {
                res = " <T>";
            } else {
                res = "<T extends " + returnType.getName() + ">";
            }
        }
        return res;
    }

    /**
     * 字典升序排序
     */
    protected static class SortByName implements Comparator {
        public int compare(Object o1, Object o2) {
            String s1 = (String) o1;
            String s2 = (String) o2;
            return s1.compareTo(s2);
        }
    }
}
